package com.atguigu.tingshu.account.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.account.mapper.RechargeInfoMapper;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.account.RechargeInfo;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {


	@Autowired
	private UserAccountMapper userAccountMapper;
	@Autowired
	private UserAccountDetailMapper userAccountDetailMapper;


	@Autowired
	private StringRedisTemplate redisTemplate;

	@Autowired
	private RechargeInfoMapper rechargeInfoMapper;

	@Override
	public BigDecimal getAvailableAmount() {
		Long userId = AuthContextHolder.getUserId();
		LambdaQueryWrapper<UserAccount> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(UserAccount::getUserId,userId);
		UserAccount userAccount = userAccountMapper.selectOne(wrapper);
		return userAccount.getAvailableAmount();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Result<AccountLockResultVo> checkAndLockAmount(AccountLockVo accountLockVo) {
		//1.查询余额是否充足
		//伪sql
		//2.1如果余额不充足 余额不充足
		//2.2如果余额充足 锁定余额
		//伪sql
		Long userId = accountLockVo.getUserId();
		BigDecimal amount = accountLockVo.getAmount();
		String content = accountLockVo.getContent();
		String orderNo = accountLockVo.getOrderNo();
		String dataKey = "user:account:amount" +orderNo;
		String lockKey = "user:account:amount:lock" + orderNo;

		Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(lockKey, "1");
		if (!aBoolean){
			return Result.build(null,ResultCodeEnum.ACCOUNT_LOCK_REPEAT);
		}
		//1.江畔端余额是否充足和修改余额作为原子操作 利用CAS思想(比较并交换)
		try {
			String result = redisTemplate.opsForValue().get(dataKey);
			if (!StringUtils.isEmpty(result)){
				return Result.build(JSONObject.parseObject(result, AccountLockResultVo.class),ResultCodeEnum.ACCOUNT_LOCK_REPEAT);
			}
			int count = userAccountMapper.checkAndLockAmount(userId,amount);
			//1.1如果锁定失败
			if (count == 0){
				return Result.build(null, ResultCodeEnum.ACCOUNT_LOCK_ERROR);
			}
			//1.2锁定成功 AccountLockResultVo
			AccountLockResultVo accountLockResultVo = new AccountLockResultVo();
			accountLockResultVo.setUserId(userId);
			accountLockResultVo.setAmount(amount);
			accountLockResultVo.setContent(content);

			//1.4记录用户账户的明细(为了以后能知道账户到底花钱干了什么事情)
			this.log(userId,amount,"锁定："+content, orderNo,"1202");

			//1.3 将锁定结果对象AccountLockResultVo存储到Redis中(方便解锁以及真正的扣除余额)
			//保存到redis中(主要为了解锁和消费的时候取数据方便)
			redisTemplate.opsForValue().set(dataKey, JSONObject.toJSONString(accountLockResultVo));
			//1.5返回账户对象
			return Result.ok(accountLockResultVo);
		}catch (Exception e){
			throw new GuiguException(400,"服务内部处理数据出现异常");
		}finally {
			redisTemplate.delete(lockKey);
		}
	}
	@Override
	public void log(Long userId,BigDecimal amount, String content, String orderNo,String tradeType) {
		UserAccountDetail userAccountDetail = new UserAccountDetail();
		userAccountDetail.setUserId(userId);
		userAccountDetail.setTitle(content);
		userAccountDetail.setTradeType(tradeType);
		userAccountDetail.setAmount(amount);
		userAccountDetail.setOrderNo(orderNo);
		userAccountDetailMapper.insert(userAccountDetail);
	}

	@Override
	public RechargeInfo getRechargeInfoByOrderNo(String orderNo, Long userId) {
		LambdaQueryWrapper<RechargeInfo> wrapper = new LambdaQueryWrapper<>();
		wrapper.eq(RechargeInfo::getOrderNo,orderNo);
		wrapper.eq(RechargeInfo::getUserId,userId);
		RechargeInfo rechargeInfo = rechargeInfoMapper.selectOne(wrapper);
		return rechargeInfo;
	}

	@Override
	public IPage<UserAccountDetail> findUserConsumePage(IPage<UserAccountDetail> page, Long userId) {
		return userAccountDetailMapper.findUserConsumePage(page,userId);
	}

	@Override
	public IPage<UserAccountDetail> findUserRechargePage(IPage<UserAccountDetail> page, Long userId) {
		return userAccountDetailMapper.findUserRechargePage(page,userId);
	}


}
